#include "stdc/stdc++.h"
/**
 * @brief 享元模式（Flyweight Design Pattern）
 * 意图：通过共享多个对象间的共同状态而不是在每个对象中保留所有数据，以便将更多对象适应可用RAM的数量。
 */

// 共享状态
struct SharedState
{
    std::string brand_;
    std::string model_;
    std::string color_;

    SharedState(const std::string &brand, const std::string &model, const std::string &color) : brand_(brand), model_(model), color_(color) {}

    friend std::ostream &operator<<(std::ostream &os, const SharedState &ss)
    {
        return os << "[ " << ss.brand_ << " , " << ss.model_ << " , " << ss.color_ << " ]";
    }
};

// 非共享状态
struct UniqueState
{
    std::string owner_;
    std::string plates_;

    UniqueState(const std::string &owner, const std::string &plates) : owner_(owner), plates_(plates) {}

    friend std::ostream &operator<<(std::ostream &os, const UniqueState &us)
    {
        return os << "[ " << us.owner_ << " , " << us.plates_ << " ]";
    }
};

// brand model color 作为车的属性 是不会改变的状态
// owner plates 是会改变的状态

// 享元类 : 分别构造出 共享的状态 和 会改变的状态
class Flyweight
{
   private:
    SharedState *shared_state_;

   public:
    Flyweight(const SharedState *shared_state) : shared_state_(new SharedState(*shared_state)) {}
    Flyweight(const Flyweight &other) : shared_state_(new SharedState(*other.shared_state_)) {}
    ~Flyweight()
    {
        delete shared_state_;
    }
    SharedState *shared_state() const
    {
        return shared_state_;
    }
    void Operation(const UniqueState &unique_state) const
    {
        std::cout << "Flyweight: 显示共享状态（" << *shared_state_ << "）和非共享状态（" << unique_state << "）。\n";
    }
};

// 享元工厂类
class FlyweightFactory
{
    // 存储享元对象
   private:
    std::unordered_map<std::string, Flyweight> flyweights_;
    /**
     * 返回给定状态的Flyweight的字符串哈希值。
     */
    std::string GetKey(const SharedState &ss) const
    {
        return ss.brand_ + "" + ss.model_ + "" + ss.color_;
    }

   public:
    // 初始化享元工厂
    FlyweightFactory(std::initializer_list<SharedState> share_states)
    {
        for (const SharedState &ss : share_states)
        {
            this->flyweights_.insert(std::make_pair<std::string, Flyweight>(this->GetKey(ss), Flyweight(&ss)));
        }
    }

    /**
     * 获取存在的享元或创建新的享元。
     */
    Flyweight GetFlyweight(const SharedState &shared_state)
    {
        std::string key = this->GetKey(shared_state);
        if (this->flyweights_.find(key) == this->flyweights_.end())
        {
            std::cout << "FlyweightFactory: 找不到享元，创建新的。\n";
            this->flyweights_.insert(std::make_pair(key, Flyweight(&shared_state)));
        }
        else
        {
            std::cout << "FlyweightFactory: 正在重用现有的享元。\n";
        }
        return this->flyweights_.at(key);
    }

    // 列出所有的享元
    void ListFlyweights() const
    {
        size_t count = this->flyweights_.size();
        std::cout << "\nFlyweightFactory: 我有 " << count << " 个享元:\n";
        for (std::pair<std::string, Flyweight> pair : this->flyweights_)
        {
            std::cout << pair.first << "\n";
        }
    }
};

// 添加车辆信息到警方数据库 ： 核心是根据不变的状态生成索引 对应改变 会发生变化的状态
void AddCarToPoliceDatabase(FlyweightFactory &ff, const std::string &plates, const std::string &owner, const std::string &brand,
                            const std::string &model, const std::string &color)
{
    std::cout << "\nClient: 添加一个车辆到数据库。\n";
    const Flyweight &flyweight = ff.GetFlyweight({brand, model, color});
    // 客户端代码存储或计算非共享状态，并将其传递给享元的方法。
    flyweight.Operation({owner, plates});
}

// 主函数
int main()
{
    FlyweightFactory *factory = new FlyweightFactory({{"Chevrolet", "Camaro2018", "pink"},
                                                      {"Mercedes Benz", "C300", "black"},
                                                      {"Mercedes Benz", "C500", "red"},
                                                      {"BMW", "M5", "red"},
                                                      {"BMW", "X6", "white"}});
    factory->ListFlyweights();

    AddCarToPoliceDatabase(*factory, "CL234IR", "James Doe", "BMW", "M5", "red");

    AddCarToPoliceDatabase(*factory, "CL234IR", "James Doe", "BMW", "X1", "red");
    factory->ListFlyweights();
    delete factory;

    return 0;
}